'use strict'

// 快捷键
function keyCodeFunc(event) {
  return new Promise((resolve) => {
    if (event.ctrlKey && event.keyCode === 83) {
      event.preventDefault()
      event.returnValue = false
      resolve('ctrl+s')
    } else if (event.ctrlKey && event.keyCode === 68) {
      event.preventDefault()
      event.returnValue = false
      resolve('ctrl+d')
    } else if (event.ctrlKey && event.keyCode === 65) {
      event.preventDefault()
      event.returnValue = false
      resolve('ctrl+a')
    } else if (event.keyCode === 32) {
      // "按下空格"
      event.preventDefault()
      event.returnValue = false
      resolve('space')
    } else if (event.ctrlKey && event.keyCode === 67) {
      // console.log("按下ctrl+c");
      event.preventDefault()
      event.returnValue = false
    } else if (event.ctrlKey && event.keyCode === 86) {
      // console.log("按下ctrl+v");
      event.preventDefault()
      event.returnValue = false
    } else if (event.ctrlKey && event.keyCode === 90) {
      // console.log("按下ctrl+z");
      event.preventDefault()
      event.returnValue = false
      resolve('ctrl+z')
    } else if (event.keyCode === 46) {
      // console.log("按下delete");
      event.preventDefault()
      event.returnValue = false
      resolve('delete')
    } else if (event.keyCode === 27) {
      // console.log("按下esc");
      event.preventDefault()
      event.returnValue = false
      resolve('esc')
    } else if (event.keyCode === 112) {
      // console.log("按下f1");
      event.preventDefault()
      event.returnValue = false
    }
  })
}

export class CanvasPenTool {
  dom = ''
  imgUrl = ''
  width = '100%'
  height = '100%'
  quality = 1 // 图片质量 0 - 1
  rectMode = false
  scale = undefined // 缩放比列
  edgeClip = true // 是否进行顶点切割
  checkedPinot = undefined
  isClosePath = undefined // 是否已经闭合路径
  Model = undefined
  Canvas = undefined
  Image = undefined
  ScaleShow = undefined
  imgOffsetX = undefined
  imgOffsetY = undefined
  anchorPoints = undefined // 锚点列表
  moveImgMode = undefined
  startPosition = undefined
  pointDiv = undefined // 放锚点的容器
  onSave = () => {}
  constructor(params) {
    for (const key in params) {
      if (this[key] !== undefined) {
        this[key] = params[key]
      }
    }
    this.scale = 0
    this.imgOffsetX = 0
    this.imgOffsetY = 0
    this.createTools()
    if (this.rectMode) {
      this.addRectClipPoint()
    }
  }

  // 添加矩形裁切的四个点
  addRectClipPoint() {
    // 方形裁剪模式
    this.Image.onload = () => {
      this.mouseDownEvent({
        offsetX: this.Model.clientWidth / 4,
        offsetY: this.Model.clientHeight / 4,
        button: 0
      })
      this.mouseDownEvent({
        offsetX: (this.Model.clientWidth * 3) / 4,
        offsetY: this.Model.clientHeight / 4,
        button: 0
      })
      this.mouseDownEvent({
        offsetX: (this.Model.clientWidth * 3) / 4,
        offsetY: (this.Model.clientHeight * 3) / 4,
        button: 0
      })
      this.mouseDownEvent({
        offsetX: this.Model.clientWidth / 4,
        offsetY: (this.Model.clientHeight * 3) / 4,
        button: 0
      })
      this.finishCtxDraw()
    }
  }

  changeClipMode(mode = 'rect') {
    this.rectMode = mode === 'rect' ? true : false
    if (this.anchorPoints) {
      for (const p of this.anchorPoints) {
        this.Model.removeChild(p)
      }
      this.anchorPoints = undefined
    }
    if (this.isClosePath) {
      this.isClosePath = undefined
      this.Image.style.opacity = ''
    }
    this.ctxDraw()
    if (this.rectMode) {
      this.addRectClipPoint()
    }
  }

  createTools() {
    if (!this.imgUrl || !document.querySelector(this.dom)) return
    // 创建容器
    const Model = document.createElement('div')
    Model.tabIndex = 1
    Model.className = 'tools-model'
    Model.style.width = this.width
    Model.style.height = this.height
    Model.style.overflow = 'hidden'
    Model.style.outline = 'none'
    Model.style.position = 'relative'
    Model.style.background = 'url("' + require('./tr_bg.png') + '")'
    Model.style.display = 'flex'
    Model.style.alignItems = 'center'
    Model.style.justifyContent = 'center'
    Model.style.border = '1px solid #e1e1e1'

    const ScaleShow = (this.ScaleShow = document.createElement('div'))
    ScaleShow.style.position = 'absolute'
    ScaleShow.style.padding = '3px 10px'
    ScaleShow.style.left = '10px'
    ScaleShow.style.bottom = '10px'
    ScaleShow.style.background = 'rgba(66,66,66,.4)'
    ScaleShow.style.color = '#fff'
    ScaleShow.style.zIndex = '9'
    ScaleShow.innerText = 'scale:1.0'
    ScaleShow.style.pointerEvents = 'none'
    Model.appendChild(ScaleShow)

    // 放置下层图片
    const Image = document.createElement('img')
    Image.className = 'origin-image'
    Image.style.maxWidth = '100%'
    Image.style.maxHeight = '100%'
    Image.style.position = 'absolute'
    Image.src = this.imgUrl
    Image.style.userSelect = 'none'
    // 去除图片拖拽事件
    Image.ondragstart = () => {
      return false
    }
    Model.appendChild(Image)
    document.querySelector(this.dom).append(Model)
    Model.onfocus = () => {
      Model.onkeydown = (event) => this.keyDownEvent(event)
      Model.onkeyup = (event) => this.keyUpEvent(event)
      Model.onmousedown = (event) => this.mouseDownEvent(event)
      Model.onmousewheel = (event) => this.mouseWheelEvent(event)
      Model.onmouseup = (event) => this.mouseUpEvent(event)
    }
    Model.onblur = () => {
      Model.onkeydown = undefined
      Model.onmousedown = undefined
    }
    Model.focus()
    this.Model = Model
    this.Image = Image

    setTimeout(() => {
      const pointDiv = (this.pointDiv = document.createElement('div'))
      pointDiv.className = 'use-points-div'
      pointDiv.width = Model.clientWidth
      pointDiv.height = Model.clientHeight
      pointDiv.style.zIndex = 3
      pointDiv.style.position = 'absolute'
      const Canvas = (this.Canvas = document.createElement('canvas'))
      Canvas.className = 'use-canvas'
      Canvas.width = Model.clientWidth
      Canvas.height = Model.clientHeight
      Canvas.style.zIndex = 2
      Canvas.style.position = 'absolute'
      Model.appendChild(Canvas)
      Model.appendChild(pointDiv)
    })
  }

  /**
   * 鼠标滚轮事件
   */
  mouseWheelEvent(event) {
    event.stopPropagation()
    event.preventDefault()
    // if (this.isClosePath) return; // 不可编辑返回
    if (event.deltaY > 0) {
      // 往下滚
      if (this.scale === 8) return
      this.scale += 1
    } else {
      // 往上滚
      this.scale -= 1
    }
    this.ScaleShow.innerText = 'scale:' + (1 - this.scale * 0.1).toFixed(1)
    this.Image.style.maxWidth = `calc(100% - 10% * ${this.scale})`
    this.Image.style.maxHeight = `calc(100% - 10% * ${this.scale})`
    if (this.anchorPoints) {
      this.dealPointsScale()
    }
  }

  /**
   * 处理锚点阵的缩放
   */
  dealPointsScale() {
    // centerX，centerY 图片中心点
    const centerX = this.Image.width / 2 + this.Image.offsetLeft + this.imgOffsetX
    const centerY = this.Image.height / 2 + this.Image.offsetTop + this.imgOffsetY
    for (const p of this.anchorPoints) {
      const distanceX = p.ScaleX * (1 - this.scale * 0.1)
      const distanceY = p.ScaleY * (1 - this.scale * 0.1)
      p.X > centerX ? (p.X = centerX + distanceX) : (p.X = centerX - distanceX)
      p.Y > centerY ? (p.Y = centerY + distanceY) : (p.Y = centerY - distanceY)
      p.style.left = p.X - 4 + 'px'
      p.style.top = p.Y - 4 + 'px'
    }
    if (this.isClosePath) {
      this.finishCtxDraw()
    } else {
      this.ctxDraw()
    }
  }

  /**
   * 添加键盘事件
   */
  keyDownEvent(event) {
    keyCodeFunc(event).then((res) => {
      if (res === 'ctrl+z') {
        if (this.rectMode) return // 矩形裁剪不能删掉点
        if (this.isClosePath) {
          this.isClosePath = undefined
          this.Image.style.opacity = ''
        }
        if (!this.anchorPoints || this.anchorPoints.length === 0) return
        this.Model.removeChild(this.anchorPoints[this.anchorPoints.length - 1])
        this.anchorPoints.pop()
        this.ctxDraw() // 绘制canvas
      } else if (res === 'ctrl+d') {
        if (this.rectMode) return // 矩形裁剪不能删掉点
        if (this.isClosePath) {
          this.isClosePath = undefined
          this.Image.style.opacity = ''
        }
        for (const p of this.anchorPoints) {
          this.Model.removeChild(p)
        }
        this.anchorPoints = undefined
        this.ctxDraw()
      } else if (res === 'ctrl+s') {
        if (this.isClosePath) {
          this.save().then((canvasData) => {
            this.onSave(canvasData)
          })
        }
      } else if (res === 'space') {
        // if (this.isClosePath) return;
        this.Model.style.cursor = 'move'
        this.moveImgMode = true
        if (this.anchorPoints) {
          for (const p of this.anchorPoints) {
            p.style.pointerEvents = 'none'
          }
        }
      }
    })
    return
  }

  /**
   *
   */
  keyUpEvent(event) {
    if (event.keyCode === 32) {
      if (/* !this.isClosePath && */ this.moveImgMode) {
        this.Model.style.cursor = 'default'
        event.preventDefault()
        event.returnValue = false
        this.moveImgMode = false
        this.Model.onmousemove = undefined

        if (this.anchorPoints) {
          for (const p of this.anchorPoints) {
            p.style.pointerEvents = 'unset'
          }
        }
      }
    }
  }

  /**
   * 添加鼠标弹起事件
   */
  mouseUpEvent(event) {
    this.Model.onmousemove = undefined
    if (/* !this.isClosePath && */ this.moveImgMode) {
      this.Model.style.cursor = 'default'
      event.preventDefault()
      event.returnValue = false
      this.moveImgMode = false
      this.Model.onmousemove = undefined
      const thisOffsetX = event.offsetX - this.startPosition.offsetX
      const thisOffsetY = event.offsetY - this.startPosition.offsetY
      this.imgOffsetX += thisOffsetX
      this.imgOffsetY += thisOffsetY

      if (this.anchorPoints) {
        for (const p of this.anchorPoints) {
          p.style.pointerEvents = 'unset'
        }
      }
      return
    }
    if (!this.checkedPinot) return
    for (const p of this.anchorPoints) {
      p.style.pointerEvents = 'unset'
      p.style.border = '1px solid #333'
      p.style.background = '#f2f2f2'
      if (this.anchorPoints.indexOf(p) === 0) {
        p.style.border = '1px solid #fe2222'
      }
    }
    this.checkedPinot.style.left = event.offsetX - 4 + 'px'
    this.checkedPinot.style.top = event.offsetY - 4 + 'px'
    this.checkedPinot = undefined
  }

  /**
   * 添加点击事件
   */
  mouseDownEvent(event) {
    if (this.moveImgMode /* && !this.isClosePath */) {
      if (this.anchorPoints) this.startAnchorPoints = JSON.parse(JSON.stringify(this.anchorPoints))
      this.startPosition = event
      this.Model.onmousemove = (event) => this.mouseMoveEvent(event)
      return
    }
    if (this.checkedPinot || event.button !== 0 || this.isClosePath) return
    if (!this.anchorPoints) {
      this.anchorPoints = []
    }
    const p = this.getAnchorPoint(event.offsetX, event.offsetY)
    p.onmousedown = (e) => {
      e.preventDefault()
      e.stopPropagation()
      if (p === this.anchorPoints[0] && this.anchorPoints.length >= 3) {
        this.finishCtxDraw()
        // return;
      }
      this.checkedPinot = p
      for (const p of this.anchorPoints) {
        p.style.pointerEvents = 'none'
      }
      p.style.border = '1px solid #1A73E8'
      p.style.background = '#C1D8F7'
      this.Model.onmousemove = (event) => this.mouseMoveEvent(event)
    }
    this.anchorPoints.push(p)
    this.Model.appendChild(p)
    this.ctxDraw() // 绘制canvas
  }

  /**
   * canvas开始划线
   */
  ctxDraw() {
    this.Canvas.width = this.Model.clientWidth
    this.Canvas.height = this.Model.clientHeight
    if (!this.anchorPoints || this.anchorPoints.length === 0) return
    const ctx = this.Canvas.getContext('2d')
    ctx.strokeStyle = '#1984ec'
    ctx.moveTo(this.anchorPoints[0].X, this.anchorPoints[0].Y)
    for (const p of this.anchorPoints) {
      ctx.lineTo(p.X, p.Y)
    }
    // ctx.fillRect(this.Canvas.width / 2 - 1, this.Canvas.height / 2 - 1, 2, 2)
    ctx.stroke()
  }

  /**
   * canvas结束绘制
   */
  finishCtxDraw(thisOffsetX = 0, thisOffsetY = 0) {
    this.isClosePath = true
    this.Image.style.opacity = '0.3'
    this.Canvas.width = this.Model.clientWidth
    this.Canvas.height = this.Model.clientHeight
    if (!this.anchorPoints || this.anchorPoints.length === 0) return
    const ctx = this.Canvas.getContext('2d')
    ctx.strokeStyle = '#1984ec'
    ctx.moveTo(this.anchorPoints[0].X, this.anchorPoints[0].Y)
    for (const p of this.anchorPoints) {
      ctx.lineTo(p.X, p.Y)
    }
    ctx.closePath()
    ctx.stroke()
    ctx.clip()
    ctx.drawImage(
      this.Image,
      (this.Canvas.width - this.Image.width) / 2 + this.imgOffsetX + thisOffsetX,
      (this.Canvas.height - this.Image.height) / 2 + this.imgOffsetY + thisOffsetY,
      this.Image.width,
      this.Image.height
    )
  }

  /**
   * 添加拖动事件
   */
  startAnchorPoints = undefined
  mouseMoveEvent(event) {
    if (this.moveImgMode /* && !this.isClosePath */) {
      const thisOffsetX = event.offsetX - this.startPosition.offsetX
      const thisOffsetY = event.offsetY - this.startPosition.offsetY
      this.Image.style.transform = `translate(${this.imgOffsetX + thisOffsetX}px,${this.imgOffsetY + thisOffsetY}px)`
      if (this.anchorPoints) {
        for (const i in this.anchorPoints) {
          this.anchorPoints[i].X = this.startAnchorPoints[i].X + thisOffsetX
          this.anchorPoints[i].Y = this.startAnchorPoints[i].Y + thisOffsetY
          this.anchorPoints[i].style.left = this.startAnchorPoints[i].X + thisOffsetX - 4 + 'px'
          this.anchorPoints[i].style.top = this.startAnchorPoints[i].Y + thisOffsetY - 4 + 'px'
        }
        if (this.isClosePath) {
          this.finishCtxDraw(thisOffsetX, thisOffsetY)
        } else {
          this.ctxDraw() // 绘制canvas
        }
      }
      return
    }
    if (!this.checkedPinot) return
    this.checkedPinot.X = event.offsetX
    this.checkedPinot.Y = event.offsetY
    this.checkedPinot.style.left = event.offsetX - 4 + 'px'
    this.checkedPinot.style.top = event.offsetY - 4 + 'px'
    const centerX = this.Image.width / 2 + this.Image.offsetLeft + this.imgOffsetX
    const centerY = this.Image.height / 2 + this.Image.offsetTop + this.imgOffsetY
    this.checkedPinot.ScaleX = Math.abs(centerX - this.checkedPinot.X) / (1 - this.scale * 0.1)
    this.checkedPinot.ScaleY = Math.abs(centerY - this.checkedPinot.Y) / (1 - this.scale * 0.1)
    if (this.rectMode) {
      // 矩形裁切
      for (const i in this.anchorPoints) {
        if (this.anchorPoints[i] === this.checkedPinot) {
          switch (Number(i)) {
            case 0:
              this.anchorPoints[3].X = this.checkedPinot.X
              this.anchorPoints[3].ScaleX = Math.abs(centerX - this.checkedPinot.X) / (1 - this.scale * 0.1)
              this.anchorPoints[3].style.left = event.offsetX - 4 + 'px'
              this.anchorPoints[1].Y = this.checkedPinot.Y
              this.anchorPoints[1].ScaleY = Math.abs(centerY - this.checkedPinot.Y) / (1 - this.scale * 0.1)
              this.anchorPoints[1].style.top = event.offsetY - 4 + 'px'
              break
            case 1:
              this.anchorPoints[2].X = this.checkedPinot.X
              this.anchorPoints[2].ScaleX = Math.abs(centerX - this.checkedPinot.X) / (1 - this.scale * 0.1)
              this.anchorPoints[2].style.left = event.offsetX - 4 + 'px'
              this.anchorPoints[0].Y = this.checkedPinot.Y
              this.anchorPoints[0].ScaleY = Math.abs(centerY - this.checkedPinot.Y) / (1 - this.scale * 0.1)
              this.anchorPoints[0].style.top = event.offsetY - 4 + 'px'
              break
            case 2:
              this.anchorPoints[1].X = this.checkedPinot.X
              this.anchorPoints[1].ScaleX = Math.abs(centerX - this.checkedPinot.X) / (1 - this.scale * 0.1)
              this.anchorPoints[1].style.left = event.offsetX - 4 + 'px'
              this.anchorPoints[3].Y = this.checkedPinot.Y
              this.anchorPoints[3].ScaleY = Math.abs(centerY - this.checkedPinot.Y) / (1 - this.scale * 0.1)
              this.anchorPoints[3].style.top = event.offsetY - 4 + 'px'
              break
            case 3:
              this.anchorPoints[0].X = this.checkedPinot.X
              this.anchorPoints[0].ScaleX = Math.abs(centerX - this.checkedPinot.X) / (1 - this.scale * 0.1)
              this.anchorPoints[0].style.left = event.offsetX - 4 + 'px'
              this.anchorPoints[2].Y = this.checkedPinot.Y
              this.anchorPoints[2].ScaleY = Math.abs(centerY - this.checkedPinot.Y) / (1 - this.scale * 0.1)
              this.anchorPoints[2].style.top = event.offsetY - 4 + 'px'
              break
          }
        }
      }
    }
    if (this.isClosePath) {
      this.finishCtxDraw()
    } else {
      this.ctxDraw() // 绘制canvas
    }
  }

  /**
   * @param {距离模型左上角的横轴} x
   * @param {距离模型左上角的竖轴} y
   */
  getAnchorPoint(x, y) {
    const point = document.createElement('div')
    point.className = 'anchor-point'
    point.X = x
    point.Y = y
    point.style.width = '8px'
    point.style.height = '8px'
    point.style.background = '#f2f2f2'
    point.style.borderRadius = '50%'
    point.style.position = 'absolute'
    point.style.top = y - 4 + 'px'
    point.style.left = x - 4 + 'px'
    point.style.cursor = 'pointer'
    point.style.zIndex = 4
    point.style.border = '1px solid #333'
    // centerX，centerY 图片中心点
    const centerX = this.Image.width / 2 + this.Image.offsetLeft + this.imgOffsetX
    const centerY = this.Image.height / 2 + this.Image.offsetTop + this.imgOffsetY
    point.ScaleX = Math.abs(centerX - x) / (1 - this.scale * 0.1)
    point.ScaleY = Math.abs(centerY - y) / (1 - this.scale * 0.1)
    if (!this.anchorPoints || this.anchorPoints.length === 0) {
      point.style.border = '1px solid #fe2222'
    }
    return point
  }

  save(callback) {
    if (!this.isClosePath) return //需要闭合了才能save
    const isCheckWidth = this.Image.naturalWidth / this.Image.naturalHeight > this.Canvas.width / this.Canvas.height
    // 画布和实际图片的比例
    const bitPresent =
      (isCheckWidth
        ? this.Image.naturalWidth / this.Canvas.width
        : this.Image.naturalHeight / this.Canvas.height
      ).toFixed(2) * this.quality // *图片质量
    if (this.edgeClip) {
      // 需要矩形裁切
      const newCanvas = document.createElement('canvas')
      let minX = this.anchorPoints[0].X,
        minY = this.anchorPoints[0].Y,
        maxX = this.anchorPoints[0].X,
        maxY = this.anchorPoints[0].Y
      for (const p of this.anchorPoints) {
        if (minX > p.X) {
          minX = p.X
        }
        if (minY > p.Y) {
          minY = p.Y
        }
        if (maxX < p.X) {
          maxX = p.X
        }
        if (maxY < p.Y) {
          maxY = p.Y
        }
      }
      newCanvas.width = (maxX - minX) * bitPresent
      newCanvas.height = (maxY - minY) * bitPresent
      const ctx = newCanvas.getContext('2d')
      ctx.moveTo(
        this.anchorPoints[0].X * bitPresent - minX * bitPresent,
        this.anchorPoints[0].Y * bitPresent - minY * bitPresent
      )
      for (const p of this.anchorPoints) {
        ctx.lineTo(p.X * bitPresent - minX * bitPresent, p.Y * bitPresent - minY * bitPresent)
      }
      ctx.closePath()
      ctx.clip()
      ctx.drawImage(
        this.Image,
        ((this.Canvas.width - this.Image.width) / 2 + this.imgOffsetX - minX) * bitPresent,
        ((this.Canvas.height - this.Image.height) / 2 + this.imgOffsetY - minY) * bitPresent,
        this.Image.width * bitPresent,
        this.Image.height * bitPresent
      )
      if (callback) callback(newCanvas)
      return new Promise((resolve) => {
        resolve(newCanvas)
      })
    }
    // 不需要矩形裁切
    if (callback) callback(this.Canvas)
    return new Promise((resolve) => {
      resolve(this.Canvas)
    })
  }
}
